<template>
  <div class="slider" ref="ref">
    <div
      class="slider__ball"
      ref="ballRef"
      :style="{ left: `calc(${per}% - 8px)` }"
      @mousedown.prevent="onSlide($event)"
    >
      <div class="slider__ball__tips" v-if="isMoving">{{ tooltips }}</div>
    </div>
  </div>
</template>

<script>
import { watch, reactive, toRefs } from "vue";

const getEstimate = (value) => {
  if (value <= 15) {
    return [0, "一般"];
  } else if (value <= 48.34) {
    return [33.34, "良好"];
  } else if (value <= 81.67) {
    return [66.67, "熟练"];
  }
  return [100, "精通"];
};
export default {
  name: "RSlider",
  props: {
    modelValue: [Number, String],
  },
  setup(props, ctx) {
    const reactiveObj = reactive({
      per: 0,
      tooltips: "",
      isMoving: false,
    });
    const el = reactive({
      ballRef: null,
      ref: null,
    });

    const [per, tips] = getEstimate(props.modelValue);
    Object.assign(reactiveObj, { per, tips });

    watch(reactiveObj, (newVal) => {
      ctx.emit("update:modelValue", newVal.per);
    });
    watch(
      () => props.modelValue,
      (newVal) => {
        reactiveObj.per = newVal;
      }
    );

    const onSlide = (event) => {
      const dom = el.ballRef;
      const initX = parseInt(event.clientX);
      let initLeft =
        (reactiveObj.per * parseInt(el.ref.clientWidth)) / 100 -
        parseInt(dom.clientWidth);
      const maxLeft =
        parseInt(el.ref.clientWidth) - parseInt(dom.clientWidth) / 2;
      initLeft = Number.isNaN(initLeft) ? 0 : initLeft;
      dom.style.transition = "";
      const mouseMove = (me) => {
        const newLeft = parseInt(me.clientX) - initX + initLeft;
        if (newLeft >= 0 && newLeft <= maxLeft) {
          dom.style.left = `${newLeft}px`;
          const doneVal =
            (parseInt(dom.style.left) * 100) / parseInt(el.ref.clientWidth);
          const [, tips] = getEstimate(doneVal);
          reactiveObj.tooltips = tips;
          reactiveObj.isMoving = true;
        }
      };
      document.addEventListener("mousemove", mouseMove);

      document.addEventListener(
        "mouseup",
        () => {
          document.removeEventListener("mousemove", mouseMove);
          const doneVal =
            (parseInt(dom.style.left) * 100) / parseInt(el.ref.clientWidth);
          const [finalVal] = getEstimate(doneVal);
          dom.style.left = finalVal > 0 ? `calc(${finalVal}% - 8px)` : "0";
          dom.style.transition = "left 0.5s";
          Object.assign(reactiveObj, { per: finalVal, isMoving: false });
        },
        { once: true }
      );
    };

    return {
      ...toRefs(reactiveObj),
      ...toRefs(el),
      onSlide,
    };
  },
  methods: {},
};
</script>

<style scoped lang="less">
@ballWidth: 16px;
@halfWith: (@ballWidth / 2);

.slider {
  height: 10px;
  width: 100px;
  background: lightgrey;
  border-radius: 5px;
  position: relative;
  margin-right: @halfWith;

  .slider__ball {
    top: -3px;
    height: @ballWidth;
    width: @ballWidth;
    background: lightskyblue;
    border-radius: @halfWith;
    position: relative;
    cursor: pointer;

    &__tips {
      position: absolute;
      top: -23px;
      left: -8px;
      background: rgba(0, 0, 0, 0.8);
      width: 28px;
      padding: 2px;
      border-radius: 2px;
      color: white;
      text-align: center;

      &::selection {
        background: none;
      }
    }
  }
}
</style>
